/**
 * Copyright (C) 2008 Christopher Allen Ogden
 *
 *     This file is part of Odysi.
 *
 *  Odysi is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Odysi is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Odysi.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <string>
#include <vector>
#include <map>
#include "uncopyable.h"
#include "object.h"
#include "cache.h"
#include "mapFileFormat.h"

/**
 * Stores a map.
 *
 * @author Ogden, Chris
 */
class Map:private Uncopyable
{
    public:
        /**
         * Default constructor.
         */
        Map(const std::string &fileName);

        /**
         * Destructor.
         */
        ~Map();

        /**
         * Add an object to the map.
         *
         * @param object    The object to add.
         * @param permanent True if the object should be added to the map file.
         */
        void addObject(Object *object, bool permanent = false);

        /**
         * Get an object by ID.
         *
         * @param id    ID of object to get.
         * @return      Pointer to object.
         */
        Object *getDynamicObject(int id) const;

        typedef std::vector<Object *> ObjectList;
        ObjectList getDynamicObjects(void) const;

        typedef std::vector<Object> CachedObjectList;
        const CachedObjectList * getStaticObjects(int x, int y);

        /**
         * Determine if a location can be walked to.
         *
         * @param startX    The X location starting from.
         * @param startY    The Y location starting form.
         * @param endX      The X location going to.
         * @param endY      The Y location going to.
         * @param z         The Z location of the character.
         * @param h         The height of the character.
         * @return          True if you can walk, false otherwise.
         */
        bool canWalk(int startX, int startY, int endX, int endY, int z, int h);

        /**
         * Clear the map.
         */
        void clear(void);

        /**
         * Delete all objects from a location.
         *
         * @param x The X location.
         * @param y The Y location.
         */
        void deleteObjects(int x, int y);

        /**
         * Draw a location.
         *
         * @param startX    The X coordinate to start drawing from.
         * @param startY    The Y coordinate to start drawing from.
         * @param endX      The end X coordinate to start drawing from.
         * @param endY      The end Y coordinate to start drawing from.
         * @param xOffset   The X offset to draw the map from.
         * @param yOffset   The Y offset to draw the map from.
         */
        void draw(int startX, int startY, int endX, int endY, int xOffset, int yOffset);

        /**
         * Get an available object ID.
         *
         * @return  An available object ID.
         */
        unsigned int getAvailableID(void) const;

        /**
         * Get the tile ID at a coordinate.
         *
         * @param x     The X coordinate to get the ID of.
         * @param y     The Y coordinate to get the ID of.
         * @param layer The layer to get.
         * @return      The tile ID.
         */
        unsigned short getID(int x, int y, int layer);

        /**
         * Get the height.
         *
         * @return  The height of the map.
         */
        unsigned int getHeight(void) const;

        /**
         * Get the width.
         *
         * @return  The width of the map.
         */
        unsigned int getWidth(void) const;

        /**
         * Set a tile.
         *
         * @param x     The X location.
         * @param y     The Y location.
         * @param layer The layer.
         * @param id    The new tile ID.
         */
        void setID(int x, int y, int layer, unsigned short id);

        /**
         * Tick.
         */
        void tick(void);

        /**
         * Get the base height.
         *
         * @param x The X location.
         * @param y The Y location.
         * @param z The Z location.
         * @param h The height.
         */
        unsigned int getBlockedHeight(int x, int y, unsigned int z, int h);
    private:
        Map();

        /**
         * Check if a tile can be walked on given a start and
         * end location.  This method uses the walk bitmask of
         * the start location, optionally ignoring a
         * zero (no walk) tile for outbound (so that the character
         * can always walk off of solid objects if they somehow
         * got onto one).
         *
         * @param startX        The X location to start from.
         * @param startY        The Y location to start from.
         * @param endX          The X location going to.
         * @param endY          The Y location going to.
         * @param z             The Z location of the character.
         * @param h             The height of the character.
         * @param ignoreZero    Whether or not to ignore zero.
         * @return              True if you can walk, false otherwise.
         */
        bool canLeave(int startX, int startY, int endX, int endY, int z, int h, bool ignoreZero = false);

        /**
         * Returns the combined bitmask for the ground tile and
         * all objects at a specific location.
         *
         * @param x The X location.
         * @param y The Y location.
         * @param z The Z location.
         * @param h The height.
         * @return  The bitmask.
         */
        int getBitmask(int x, int y, int z, int h);

        // Map dimensions.
        unsigned int _width;
        unsigned int _height;
        unsigned int _widthBlocks;
        unsigned int _heightBlocks;

        // Objects.
        typedef Cache<unsigned int, CachedObjectList> ObjectCache;
        ObjectCache _objectCache;
        ObjectList _dynamicObject;

        typedef Cache<unsigned int, MapFileFormat::Block> BlockCache;
        BlockCache _blockCache;

        MapFileFormat::Block *_lastBlock;
        unsigned int _lastKey;

        std::string _mapFile;
};

